package me.maven.plugin.remotejar;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.nio.file.Files;
import java.util.regex.Pattern;

/**
 * @author langle__
 * @version 1.0
 */
public class ClientThread implements Runnable {

    private static final int MAX_FILENAME_LENGTH = 200;
    private static final String ILLEGAL_CHARS = "[\\\\/:*\"<>|]";
    private static final Pattern CONTROL_CHAR_PATTERN = Pattern.compile("\\p{Cntrl}");
    private static final Pattern ILLEGAL_UNICODE_PATTERN = Pattern.compile("[\\u2000-\\u200F\\u2028-\\u202F\\u205F-\\u206F\\uFEFF]");

    private static final Logger logger = Main.getLogger();
    private final Socket client;
    private final File baseDir;
    private final String address;
    private final int port;

    public ClientThread(Socket client, File dir) {
        this.client = client;
        this.address = client.getInetAddress().getHostAddress();
        this.port = client.getPort();
        this.baseDir = dir;
    }

    @Override
    public void run() {
        try (
                DataInputStream input = new DataInputStream(client.getInputStream());
                DataOutputStream output = new DataOutputStream(client.getOutputStream())
        ) {
            ThreadContext.put("client", "[" + this.getAddress() + ":" + this.getPort() + "]");

            JSONObject jsonPassword = JSON.parseObject(input.readUTF());
            String password = jsonPassword.getString("password");
            boolean result = password.equals(Main.getPassword());

            JSONObject jsonResult = new JSONObject();
            jsonResult.put("result", result);
            output.writeUTF(JSON.toJSONString(jsonResult));

            if (!result) {
                logger.info("Password error");
                return;
            }

            JSONObject jsonFile = JSON.parseObject(input.readUTF());
            String fileName = jsonFile.getString("name");
            fileName = removeExtension(fileName);
            if (!isValidFileName(fileName)) {
                return;
            }
            File file = new File(baseDir, fileName);
            long fileLength = jsonFile.getLong("length");

            long fileLengthInKB = fileLength >= 1024 ? fileLength / 1024 : 1;
            logger.info("Receiving file: {} size: {} KB", fileName, fileLengthInKB);
            try (
                    BufferedOutputStream fileOutput = new BufferedOutputStream(Files.newOutputStream(file.toPath()))
            ) {
                byte[] buffer = new byte[8192]; // 使用8KB的缓冲区
                int bytesRead;
                while ((bytesRead = input.read(buffer)) != -1) {
                    fileOutput.write(buffer, 0, bytesRead);
                }
                logger.info("Receiving file: {} success", fileName);
            } catch (Exception e) {
                logger.info("Receiving file: {} failed", fileName);
            }
        } catch (Exception ignored) {
        }
        // client 关闭
        try {
            client.close();
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }

    public String getAddress() {
        return address;
    }

    public int getPort() {
        return port;
    }

    public void setSoTimeout(int timeout) throws SocketException {
        this.client.setSoTimeout(timeout);
    }

    /**
     * 检测字符串是否包含非法字符
     *
     * @param fileName 要检查的文件名
     * @return 如果文件名有效则返回true，否则返回false
     */
    public static boolean isValidFileName(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return false;
        }
        // 检查文件名长度
        if (fileName.length() > MAX_FILENAME_LENGTH) {
            return false;
        }
        // 检查文件名是否包含非法字符
        if (fileName.matches(".*" + ILLEGAL_CHARS + ".*")) {
            return false;
        }
        // 禁止零字节和其他控制字符
        if (CONTROL_CHAR_PATTERN.matcher(fileName).find()) {
            return false;
        }
        // 禁止其他非法 Unicode 字符
        if (ILLEGAL_UNICODE_PATTERN.matcher(fileName).find()) {
            return false;
        }
        return true;
    }

    public static String removeExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return fileName;
        }

        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex == -1) {
            return fileName; // 没有后缀
        }

        return fileName.substring(0, lastDotIndex) + ".jar";
    }

}
